/*
 SGSortingGameViewController.m
 Date Created: Feburary 24, 2010
 
 Authors: Karanvir Gill <viper1029@gmail.com>
	        Frank Lau <flaua@sfu.ca>
          Karol Krizka <kkrizka@gmail.com>
 
 Copyright (C) 2010 Green-Power
 
 URL: http://code.google.com/p/green-power
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
 $Id$
 
 */

#import "SGSortingGameViewController.h"
#import "SGPauseViewController.h"
#import "SGTutorialModeViewController.h"
#import "SGRecyclableType.h"
#import "SGBinType.h"
#import "SGBin.h"
#import "SGSummaryViewController.h"
#import "SGEndGameViewController.h"


@implementation SGSortingGameViewController
@synthesize levelScoreLabel;
@synthesize totalScoreLabel;
@synthesize sortingGameLevel;
@synthesize livesLeftLabel;
@synthesize levelLabel;
@synthesize itemsLeftLabel;

@synthesize playerProfile;
@synthesize livesLeft;

- (SGSortingGameViewController*)init{
	self = [super init];
	if( self ) {
    
		// Initialize instance variables
		currLevel = 1;//initial level will be level 1 
    totalScore = 0;
    totalCorrectlySortedInGame = 0;
    totalProcessedInGame = 0;
    levelChanged=YES;
    tutorialModeOn=YES;
		
		// Create arrays of x and y values for progress chart that will be shown at the end of each level and the game 		
		xValues = [[NSMutableArray alloc] init];
		yValues = [[NSMutableArray alloc] init];
		
		// Listen for inactive notifications to pause the game on calls and screen
		// locking.
		[[NSNotificationCenter defaultCenter] addObserver:self 
																						 selector:@selector(handleNotification:)
																								 name:UIApplicationWillResignActiveNotification 
																							 object:[UIApplication sharedApplication]];	
	}
	return self;
}
	
- (void)setPlayerProfile:(PMProfile *)newPlayerProfile {
  if(playerProfile) [playerProfile release];
  playerProfile=newPlayerProfile;
  if(playerProfile) {
    [playerProfile retain];
    tutorialModeOn=!playerProfile.sortingGameLaunched;
    playerProfile.sortingGameLaunched=YES;
    [playerProfile.managedObjectContext save:nil];
  }
}

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"level %i", currLevel);
	
  binsOnScreen = [[NSMutableArray alloc] init]; // Will contain all the bins after drawBins is called
  [self drawBins];

  // Initialize the tutorial mode, all items start of as unexplained
  unexplainedItemTypes=[[SGRecyclableType listOfficialTypes] mutableCopy];
  [unexplainedItemTypes retain];
}


- (void)stopTimers {
  [createTimer invalidate];
  [moveTimer invalidate];
  [createTimer release];
  [moveTimer release]; 
}

- (void)startTimers {
  // All items shold be spaced 100 pixels appart. So the time between items is
  // distance = velocity * time
  // time = distance / velocity
  //
  // SGSortingGameLevel#speedOfItems returns speed as # pixels / 0.02 s
  float timeBetweenNewItems=100.0*0.02/((float)sortingGameLevel.speedOfItems.y);
  NSLog(@"Time %f Speed %d",timeBetweenNewItems,sortingGameLevel.speedOfItems.y);
  createTimer=[[NSTimer scheduledTimerWithTimeInterval:timeBetweenNewItems target:self selector:@selector(createItem) userInfo:nil repeats:YES] retain];
	moveTimer=[[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(moveItems) userInfo:nil repeats:YES] retain];  
}

//Controlled by a timer to create new objects at set time intervals 
- (void)createItem {
	if(numberOfItemsAlreadyDisplayed <totalNumberOfItemsToDisplay){
		
		// Determine if the item will be a life giver
		BOOL isLifeGiver=NO;
		NSInteger probabilityOfLifeGiver = (5-livesLeft);
		// If the random number is less than or equal to the probability the items is life giver
		if(probabilityOfLifeGiver !=0 && probabilityOfLifeGiver >= arc4random() % 101){
			isLifeGiver=YES;
		}
		
		SGRecyclableType *itemTypeToCreate = [arrayOfItemsToDisplay objectAtIndex:numberOfItemsAlreadyDisplayed];
		
		// Create the item and add it to the subview
		SGRecyclableItem *createdItem = [[SGRecyclableItem alloc] initWithType:itemTypeToCreate xLocation:160 yLocation:-50 givesLife:isLifeGiver];
		[createdItem setUserInteractionEnabled:YES];
		[self.view addSubview:createdItem];
		[itemsOnScreen addObject:createdItem];
		[createdItem release];
    
    // Handle tutorial mode
    if(tutorialModeOn && [unexplainedItemTypes containsObject:itemTypeToCreate]) {
      // This is an unexplained item!
      NSLog(@"Unexplained item %@",itemTypeToCreate.uniqueName);
      [unexplainedItemTypes removeObject:itemTypeToCreate];
      
      SGTutorialModeViewController *tutorialModeViewController=[[SGTutorialModeViewController alloc] initWithType:itemTypeToCreate];
      [self setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
      [self presentModalViewController:tutorialModeViewController animated:YES];
      [tutorialModeViewController.tutorialModeSwitch addTarget:self
                                                        action:@selector(tutorialModeOnOff:)
                                              forControlEvents:UIControlEventValueChanged];
      
      [tutorialModeViewController release];
    }
    numberOfItemsAlreadyDisplayed++;
    
    itemsLeftLabel.text=[NSString stringWithFormat:@"%d",(totalNumberOfItemsToDisplay-numberOfItemsAlreadyDisplayed)];
  }
}

// Loop through all of the items and move them
- (void)moveItems {
  // Check if any of the bins have to be highlighted/dehighlighted
  NSMutableArray *binsShouldBeHighlighted=[NSMutableArray arrayWithCapacity:itemsOnScreen.count];
  for(SGRecyclableItem *tempItem in itemsOnScreen) {
    SGBin *dropInBin=[self binItemIsOn:tempItem];
    if(dropInBin) [binsShouldBeHighlighted addObject:dropInBin];
  }
  for(SGBin *tempBin in binsOnScreen) {
    BOOL shouldBeHighlighted=[binsShouldBeHighlighted containsObject:tempBin];
    tempBin.isHighlighted=shouldBeHighlighted;
  }
  
	// Moves all the items
	for(int i=0;i<itemsOnScreen.count;i++){
		SGRecyclableItem *tempItem = [itemsOnScreen objectAtIndex:i];
		
		if(!tempItem.isBeingDragged) {
			tempItem.center = CGPointMake(tempItem.center.x,tempItem.center.y+speedOfItems.y);
      // Reached the end of the conveyor belt, throw into garbage
			if(tempItem.center.y > 410){
				if(tempItem.uniqueName != @"garbage"){// Check if recyclable item has been dropped into garbage
					droppedInWrongBin++;
					[wrongBinSound play];
          [self animateCheckmark:@"bad.png" fromLocation:tempItem.center];
				} else {
          [self animateCheckmark:@"good.png" fromLocation:tempItem.center];
					[correctBinSound play];
				}
				numInGarbage++;

        [tempItem removeFromSuperviewWithAnimation:SGRecyclableItemRemoveFromSuperviewAnimationStyleDropDown];
				[itemsOnScreen removeObjectAtIndex:i];
        i--; // Go over again
			}
		}
	}
	
  // Loop through all of the items and see if they have been moved by the user
  // If they have been moved, remove them and add to score if necessary
	// Checks if the item is dropped in the correct bin
	for(int i=0;i<itemsOnScreen.count;i++){
		SGRecyclableItem *tempItem = [itemsOnScreen objectAtIndex:i];
		if(!tempItem.isBeingDragged && tempItem.hasBeenDropped) {
      numDraggedAndDropped++;
      SGRecyclableItemRemoveFromSuperviewAnimationStyle removeAnimationStyle=SGRecyclableItemRemoveFromSuperviewAnimationStyleDissapear;
      
      SGBin *tempBin = [self binItemIsOn:tempItem];
      if([tempBin itemAccepted:tempItem]) {//check if items is in correct bin
		  droppedInsideBin++;
		// Update scores
        if([tempItem isLifeGiver]){// Add a life if the item is a life giver
          livesLeft++;
        }
        score += [tempItem depositValue];
        totalScore += [tempItem depositValue];
        levelScoreLabel.text = [NSString stringWithFormat:@"%i", score];
        totalScoreLabel.text = [NSString stringWithFormat:@"%i", totalScore];
        [self animateCheckmark:@"good.png" fromLocation:tempItem.center];
        [correctBinSound play];
	
	  }	else if (tempBin!=nil){// Item dropped in wrong bin
        droppedInWrongBin++;
		droppedInsideBin++;
        [self animateCheckmark:@"bad.png" fromLocation:tempItem.center];
        [wrongBinSound play];
		removeAnimationStyle=SGRecyclableItemRemoveFromSuperviewAnimationStyleDropDown;
      }
	
			
          
      if(tempBin!=nil) {
        // Only item dropped in a bin should dissapear by dropping down
        removeAnimationStyle=SGRecyclableItemRemoveFromSuperviewAnimationStyleDropDown;
      }
      
      // All user-moved items should be removed
      [tempItem removeFromSuperviewWithAnimation:removeAnimationStyle];
      [itemsOnScreen removeObjectAtIndex:i];
      i--; // We just removed the current index, so go over it again..
			
  }
	}	
	// Every item dropped outside the correct bin, inlcuding recyclable items that dropped in the garbage
	// will decrement the number of lives left by 1
	droppedOutsideCorrectBin = numDraggedAndDropped - droppedInsideBin + droppedInWrongBin;
	if(droppedOutsideCorrectBin > droppedInWrongBin){//dropped outside of a bin
		[wrongBinSound play];
	}
  totalDroppedOutsideCorrectBin += droppedOutsideCorrectBin;
	
	livesLeft -= droppedOutsideCorrectBin;
	livesLeftLabel.text = [NSString stringWithFormat:@"%i", livesLeft];
	
	// Current number of items processed
	currNumberOfItemsProcessed += numDraggedAndDropped + numInGarbage;
	
	// Reinitialize variables
	droppedOutsideCorrectBin = 0;
	numDraggedAndDropped = 0;	
	droppedInsideBin = 0;
	droppedInWrongBin = 0;
	numInGarbage = 0;
	
  
	if(livesLeft == 0) {
		NSLog(@"Game Over");
		[self showEndGame];
	}
	
  
  
	if(currNumberOfItemsProcessed == [sortingGameLevel numberOfItemsInLevel]){
		NSLog(@"End of level");
		currLevel++; 
    levelChanged=YES;

    [self showLevelSummary];	
  }
}

-(void)drawBins{
	
	NSArray *arrayWithAllBinTypes = [[NSArray alloc] initWithArray:[SGBinType listBinTypes]];
	
	// Setup the bins on the left
	int yLoc = 0;
	for(int i=0;i<4;i++) {
    SGBinType *binType=[arrayWithAllBinTypes objectAtIndex:i];
		SGBin *tempBin = [[SGBin alloc] initWithType:binType xLocation:2 yLocation:(50+yLoc)];
		[self.view addSubview:tempBin];
		[binsOnScreen addObject:tempBin];
		[tempBin release];
		yLoc = yLoc + 90;
	}
	
	// Setup the bins on the right
	yLoc = 0;
	for(int i=4;i<7;i++){
		SGBin *tempBin = [[SGBin alloc] initWithType:[arrayWithAllBinTypes objectAtIndex:i] xLocation:233 yLocation:(100+yLoc)];
		[self.view addSubview:tempBin];
		[binsOnScreen addObject:tempBin];
		[tempBin release];
		yLoc = yLoc + 90;
	}
}


- (void) reloadView {
  [self releaseBeforeReload]; // Remove any previous level

	self.sortingGameLevel = [[SGSortingGameLevel alloc] initWithLevel:currLevel]; 
	totalDroppedOutsideCorrectBin = 0;
  currNumberOfItemsProcessed = 0;
	score=0;
  numberOfItemsAlreadyDisplayed = 0;
	droppedInCorrectBin = 0;
  droppedOutsideCorrectBin = 0; 
	droppedInsideBin = 0;         
	numDraggedAndDropped = 0;    
	droppedInWrongBin = 0;       
	numInGarbage = 0;            
	speedOfItems= [sortingGameLevel speedOfItems];
	totalNumberOfItemsToDisplay= [sortingGameLevel numberOfItemsInLevel];
	livesLeft = [sortingGameLevel livesLeft];
  
  // Prepare the sound effects
  wrongBinSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:@"uh_oh" ofType:@"aiff"]] error:nil];
  correctBinSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:@"can" ofType:@"wav"]] error:nil];
	correctBinSound.volume = 0.35;
  // Buffer the sounds, so there is little lag later on
  [wrongBinSound prepareToPlay];
  [correctBinSound prepareToPlay];
  

  // Reset the labels
  levelLabel.text = [NSString stringWithFormat:@"%i", currLevel];
  levelScoreLabel.text = [NSString stringWithFormat:@"0"];
  itemsLeftLabel.text=[NSString stringWithFormat:@"%d",(totalNumberOfItemsToDisplay-numberOfItemsAlreadyDisplayed)];
	
	// Make an array of items that will be used in the current level
	NSArray* avaliableItemsInCurrentLevel = [sortingGameLevel listItemsInLevel];
	
	// Make an array of randomly chosen RecyclableType
	// The length of the array is based on totalNumberOfItemsToDisplay 
	arrayOfItemsToDisplay = [[NSMutableArray alloc] init];
	for(int i=0;i<totalNumberOfItemsToDisplay;i++){
		[arrayOfItemsToDisplay addObject:[avaliableItemsInCurrentLevel objectAtIndex:(arc4random() % avaliableItemsInCurrentLevel.count)]];
	}
	// Will keep track of which items are on screen
	itemsOnScreen = [[NSMutableArray alloc] init];
  NSLog(@"reload view");
}

- (void) releaseBeforeReload { 
  [arrayOfItemsToDisplay release];
  [self.sortingGameLevel release];
	[wrongBinSound release];
	[correctBinSound release];
  
  // Set released items to nil, so another release call on those items will not crash the program 
  arrayOfItemsToDisplay = nil;
  self.sortingGameLevel = nil;
  createTimer = nil;
  itemsOnScreen = nil;
	wrongBinSound = nil;
	correctBinSound = nil;
}

- (void)viewWillAppear:(BOOL)animated {
  if(levelChanged) {
    // Make sure to update labels before displaying view
    [self reloadView];
    levelChanged=NO;
  }
}

- (void)viewDidAppear:(BOOL)animated {
  NSLog(@"View appears");
  [self startTimers];
}

- (void)viewWillDisappear:(BOOL)animated {
  // View dissapeared, so stop the game
  [self stopTimers];
}

- (IBAction)showPauseMenu:(id)sender {	
	SGPauseViewController *pauseViewController=[[SGPauseViewController alloc] init];
  [pauseViewController loadView];
  [pauseViewController.tutorialModeSwitch setOn:tutorialModeOn animated:NO];
  [pauseViewController.tutorialModeSwitch addTarget:self
                                             action:@selector(tutorialModeOnOff:)
                                   forControlEvents:UIControlEventValueChanged];

  [[self navigationController] pushViewController:pauseViewController animated:NO];
  
  [pauseViewController release];
}

- (IBAction)tutorialModeOnOff:(id)sender {
  if([sender isKindOfClass:[UISwitch class]]) {
    UISwitch *senderSwitch=(UISwitch *)sender;
    BOOL on=senderSwitch.on;
    tutorialModeOn=on;
    [playerProfile.managedObjectContext save:nil];
  }
}

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
	[super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)showLevelSummary {
  droppedInCorrectBin = currNumberOfItemsProcessed - totalDroppedOutsideCorrectBin; // Calculates the number dropped in the correct bin at the end of a level
  accuracy = ((float)droppedInCorrectBin/(float)totalNumberOfItemsToDisplay)*100;
  totalProcessedInGame += currNumberOfItemsProcessed;
  totalCorrectlySortedInGame += droppedInCorrectBin; 
	
	[yValues addObject:[NSNumber numberWithFloat:accuracy]];
	[xValues addObject:[NSNumber numberWithInt:currLevel-1]];
	SGSummaryViewController *summaryViewController=[[SGSummaryViewController alloc] 
																									initWithCurrLevel:currLevel 
																									withAccuracy:accuracy 
																									withTotalScore:totalScore 
																									withLevelScore:score
																									withXValues:xValues
																									withYValues:yValues]; 
	
	[[self navigationController] pushViewController:summaryViewController animated:YES];
  NSLog(@"Summary View Controller has been pushed");
	[summaryViewController release];
}

- (void)showEndGame {
  [playerProfile recordScore:totalScore achievedOn:[NSDate date]];
  accuracy = (((float)currNumberOfItemsProcessed - (float)totalDroppedOutsideCorrectBin)/(float)currNumberOfItemsProcessed)*100;
  totalProcessedInGame += currNumberOfItemsProcessed;
  droppedInCorrectBin = currNumberOfItemsProcessed - totalDroppedOutsideCorrectBin; // Calculates the number dropped in the correct bin when the game ends
  totalCorrectlySortedInGame += droppedInCorrectBin;
  [yValues addObject:[NSNumber numberWithFloat:accuracy]];
	[xValues addObject:[NSNumber numberWithInt:currLevel]];

	SGEndGameViewController *endGameViewController=[[SGEndGameViewController alloc]initWithCurrLevel:currLevel  
                                                                                 withLevelAccuracy:accuracy
                                                                                 withTotalProcessed:totalProcessedInGame
                                                                                 withTotalCorrectlySorted:(NSUInteger)totalCorrectlySortedInGame 
                                                                                 withTotalScore:(NSUInteger)totalScore
                                                                                 withLevelScore:(NSUInteger)score
                                                                                 withXValues:xValues
                                                                                 withYValues:yValues ];
	[[self navigationController] pushViewController:endGameViewController animated:YES];
  NSLog(@"End Game View Controller has been pushed");
	[endGameViewController release];
}


- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
}

- (SGBin *)binItemIsOn:(SGRecyclableItem *)item {
  CGRect itemRect = [item frame];
  CGFloat itemArea = itemRect.size.height*itemRect.size.width;
  
  CGFloat bestIntersectionArea=0;
  SGBin *bestBinCandidate=nil;
  
  for(SGBin *bin in binsOnScreen) {
    CGRect binRect = [bin frame];

    // Find out how much the item and bin overlap
    CGRect intersectionRect = CGRectIntersection(itemRect, binRect);
    CGFloat intersectionArea = intersectionRect.size.height*intersectionRect.size.width;

    // If the item is more then 35 percent in the bin, then count it as
    // dropped in the bin, otherwise its dropped outside the bin
    // Also the intersection area must be bigger than intersection area with a
    // previous bin
    if(intersectionArea>itemArea*0.35 && intersectionArea > bestIntersectionArea) {
      bestIntersectionArea=intersectionArea;
      bestBinCandidate=bin;
    }
  }
  return bestBinCandidate;
}

- (void)animateCheckmark:(NSString *)imageName fromLocation:(CGPoint)pos {
  UIImage *checkImage=[UIImage imageNamed:imageName];
  UIImageView *checkImageView=[[UIImageView alloc] initWithImage:checkImage];
  checkImageView.center=pos;
  
  [UIView beginAnimations:@"checkmark" context:checkImageView];
  [UIView setAnimationDuration:1];
  [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
  [UIView setAnimationDelegate:self];
  [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
  pos.y-=50;
  checkImageView.alpha=0;
  checkImageView.center=pos;
  [UIView commitAnimations];
  
  [self.view addSubview:checkImageView];
  
  [checkImageView release];
}

// Make sure to remove checkmarks from the view after their animation finished
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
  if([animationID isEqualToString:@"checkmark"]) {
    UIView *checkImageView=(UIView *)context;
    [checkImageView removeFromSuperview];
  }
}

- (void)handleNotification:(NSNotification *)notification {
	if(!moveTimer && !createTimer) // Do not pause if game is already paused
		[self showPauseMenu:nil];
}

- (void)dealloc {
  [playerProfile release];
  [unexplainedItemTypes release];
  [correctBinSound release];
  [wrongBinSound release];
  
  [super dealloc];
}

@end
